﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using FastReflectionLib;

namespace WindowsFormsApplication1
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            var a = CustomAttributeData.GetCustomAttributes(typeof (SomeClass));
            foreach (CustomAttributeData data in a)
            {
                
            }
            var factories = CustomAttributeData.GetCustomAttributes(typeof(SomeClass))
                .Where(d => d.Constructor.DeclaringType == typeof(TestAttribute))
                .Select(d => new AttributeFactory(d)).ToList();
 

           factories.ForEach(f => f.Create());

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

    public class AttributeFactory
    {
        public AttributeFactory(CustomAttributeData data)
        {
            this.Data = data;

            var ctorInvoker = new ConstructorInvoker(data.Constructor);
            var ctorArgs = data.ConstructorArguments.Select(a => a.Value).ToArray();
            this.m_attributeCreator = () => ctorInvoker.Invoke(ctorArgs);

            this.m_propertySetters = new List<Action<object>>();
            foreach (var arg in data.NamedArguments)
            {
                var propertyAccessor = new PropertyAccessor((PropertyInfo)arg.MemberInfo);
                var value = arg.TypedValue.Value;
                this.m_propertySetters.Add(o => propertyAccessor.SetValue(o, value));
            }
        }

        public CustomAttributeData Data { get; private set; }

        private Func<object> m_attributeCreator;
        private List<Action<object>> m_propertySetters;

        public Attribute Create()
        {
            var attribute = this.m_attributeCreator();

            foreach (var setter in this.m_propertySetters)
            {
                setter(attribute);
            }

            return (Attribute)attribute;
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class TestAttribute : Attribute
    {
        public TestAttribute(string prop)
        {
            this.Prop = prop;
        }

        public TestAttribute() { }

        public string Prop { get; set; }
    }

    [Test("Hello World")]
    [Test(Prop = "Hello World")]
    public class SomeClass { }
}
